# 存储数据
# 很多程序都要求用户输入某种信息，如让用户存储游戏首选项或提
# 供要可视化的数据。不管关注点是什么，程序都把用户提供的信息
# 存储在列表和字典等数据结构中。用户关闭程序时，几乎总是要保
# 存他们提供的信息。一种简单的方式是使用模块json 来存储数据。

# 模块json 让你能够将简单的Python数据结构转储到文件中，并在
# 程序再次运行时加载该文件中的数据。你还可以使用json 在
# Python程序之间分享数据。更重要的是，JSON数据格式并非Python
# 专用的，这让你能够将以JSON格式存储的数据与使用其他编程语
# 言的人分享。这是一种轻便而有用的格式，也易于学习。

# 注意 　JSON（JavaScript Object Notation）格式最初是为
# JavaScript开发的，但随后成了一种常见格式，被包括Python在
# 内的众多语言采用。


# 使用json.dump() 和json.load()
# 我们来编写一个存储一组数的简短程序，再编写一个将这些数读取
# 到内存中的程序。第一个程序将使用json.dump() 来存储这组
# 数，而第二个程序将使用json.load() 。
# 函数json.dump() 接受两个实参：要存储的数据，以及可用于存
# 储数据的文件对象。下面演示了如何使用json.dump() 来存储数
# 字列表：
# number_writer.py

import json
numbers = [2, 3, 5, 7, 11, 13]
filename = 'number.json'
with open(filename, 'w') as f:
	json.dump(numbers, f)		
	# 注意两个参数顺序，第一个参数是要存入文件中的数据对象，第二个参数是打开状态的待写入文件对象

# 先导入模块json ，再创建一个数字列表,接着指定了要将该数字列表存储到哪个文件中。
# 通常使用文件扩展名.json来指出文件存储的数据为JSON格式。
# 接下来，以写入模式打开这个文件，让json 能够将数据写入其中
# 使用函数json.dump() 将数字列表存储到文件numbers.json中。
# 这个程序没有输出，但可以打开文件numbers.json来看看内容。数
# 据的存储格式与Python中一样

# 下面再编写一个程序，使用json.load() 将列表读取到内存中：
# number_reader.py
import json
filename = 'number.json'
with open(filename) as f:		# 注意由于json文件格式与txt不同，此处不需要指定编码格式
	numbers = json.load(f)

print(numbers)
# 这是一种在程序之间共享数据的简单方式。


# 保存和读取用户生成的数据
# 使用json 保存用户生成的数据大有裨益，因为如果不以某种方式
# 存储，用户的信息会在程序停止运行时丢失。下面来看一个这样的
# 例子：提示用户首次运行程序时输入自己的名字，并在再次运行程
# 序时记住他。
# 先来存储用户的名字：
# remember_me.py
import json
username = input("What is your name? ")

filename = 'username_list.json'
with open(filename, 'w') as f:
	json.dump(username, f)
	print(f"We'll remember you when you come back, {username}!")


# 现在再编写一个程序，向已存储了名字的用户发出问候：
# greet_user.py
import json
filename = 'username_list.json'
with open(filename) as f:
	user_name = json.load(f)
	print(f"Welcome back, {username}!")

# 使用json.load() 将存储在username.json中的信息读取到
# 变量username 中。恢复用户名后，就可以欢迎用户回来了	


# 需要将这两个程序合并到一个程序（remember_me.py）中。这个程
# 序运行时，将尝试从文件username.json中获取用户名。因此，首先
# 编写一个尝试恢复用户名的try 代码块。如果这个文件不存在，就
# 在except 代码块中提示用户输入用户名，并将其存储到
# username.json中，以便程序再次运行时能够获取：
# remember_me.py

import json
# 如果以前存储了用户名，就加载它，否则，提示用户输入用户名并存储它
filename = 'username_list.json'
try:
	with open(filename) as f:
		user_name = json.load(f)
except FileNotFoundError:
	user_name = input("What is your name? ")
	with open(filename, 'w') as f:
		json.dump(user_name, f)
		print(f"We'll remember you when you come back, {user_name}!")
else:
	print(f"Welcome back, {user_name}!")
# 这里没有任何新代码，只是将前两个示例的代码合并到了一个程序中

# 首先尝试打开文件username.json。如果该文件存在，就将
# 其中的用户名读取到内存中，再执行else 代码块，打印
# 一条欢迎用户回来的消息。用户首次运行该程序时，文件
# username.json不存在，将引发FileNotFoundError 异常。
# 因此Python将执行except 代码块，提示用户输入用户名，
# 再使用json.dump() 存储该用户名并打印一句问候语。
# 无论执行的是except 还是else 代码块，都将显示用户名和合适的问候语。


# 重构
# 你经常会遇到这样的情况：代码能够正确地运行，但通过将其划分
# 为一系列完成具体工作的函数，还可以改进。这样的过程称为重构
# 。重构让代码更清晰、更易于理解、更容易扩展。
# 要重构remember_me.py，可将其大部分逻辑放到一个或多个函数
# 中。remember_me.py的重点是问候用户，因此将其所有代码都放到
# 一个名为greet_user() 的函数中：
# remember_me.py

import json
def greet_user():
	"""问候用户，并指出其名字"""		# 使用一个文档字符串来指出程序的作用
	filename = 'username_list.json'
	try:
		with open(filename) as f:
			user_name = json.load(f)
	except FileNotFoundError:
		user_name = input("What is your name? ")
		with open(filename, 'w') as f:
			json.dump(user_name, f)
			print(f"We'll remember you when you come back, {user_name}!")
	else:
		print(f"Welcome back, {user_name}!")

greet_user()

# 这个程序更加清晰，但函数greet_user() 所做的不仅仅是问候用户，还在存储了用户名时
# 获取它、在没有存储用户名时提示用户输入

# 下面来重构greet_user() ，减少其任务。为此，首先将获取已存
# 储用户名的代码移到另一个函数中：

import json
def get_stored_username():
	"""如果存储了用户名，就获取它。"""
	
	filename = 'username.json'
	try:
		with open(filename) as f:
			username = json.load(f)
	except FileNotFoundError:
		return None
	else:
		return username

def greet_user():
	"""问候用户，并指出其名字。"""
	username = get_stored_username()
	if username:
		print(f"Welcome back, {username}!")
	else:
		username = input("What is your name? ")
		filename = 'username.json'
		with open(filename, 'w') as f:
			json.dump(username, f)
			print(f"We'll remember you when you come back, {username}!")
greet_user()

# 新增的函数get_stored_username() 目标明确,如果存储了用户名，该函数就获取并返回它；
# 如果文件username.json不存在，该函数就返回None,这是一种不错的做法：
# 函数要么返回预期的值，要么返回None 。这让我们能够使用函数的返回值做简单的测试。
# 如果成功地获取了用户名，就打印一条欢迎用户回来的消息，否则提示用户输入用户名。

# 还需要重构greet_user() 中的另一个代码块，将没有存储用户名
# 时提示用户输入的代码放在一个独立的函数中

import json
def get_stored_username():
	"""如果存储了用户名，就获取它。"""
	
	filename = 'username.json'
	try:
		with open(filename) as f:
			username = json.load(f)
	except FileNotFoundError:
		return None
	else:
		return username

def get_new_username():
	"""提示用户输入用户名"""

	username = input("What is your name? ")
	filename = "username.json"
	with open(filename, 'w') as f:
		json.dump(username, f)
	return username

def greet_user():
	"""问候用户，并指出其名字"""
	username = get_stored_username()
	if username:
		print(f"Welcome back, {username}!")
	else:
		username = get_new_username()
		print(f"We'll remember you when you come back, {username}!")

greet_user()


# 在remember_me.py的这个最终版本中，每个函数都执行单一而清晰
# 的任务。我们调用greet_user() ，它打印一条合适的消息：要么
# 欢迎老用户回来，要么问候新用户。为此，它首先调
# 用get_stored_username() ，该函数只负责获取已存储的用户名
# （如果存储了的话）。最后在必要时调用get_new_username() ，
# 该函数只负责获取并存储新用户的用户名。要编写出清晰而易于维
# 护和扩展的代码，这种划分必不可少。

